/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_components/zhiqim_upload_large.htm
 *
 * Zhiqim UploadLarge is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.uploadlarge;

import org.zhiqim.httpd.HttpContext;
import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.enumerated.LetterCase;
import org.zhiqim.kernel.extend.HashMapSS;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Bytes;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Hexs;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.codes.MD5;
import org.zhiqim.orm.ORM;
import org.zhiqim.uploadlarge.dbo.UpllFile;

/**
 * 大文件相关方法
 *
 * @version v1.0.0 @author zhuzhiyuan 2019-1-10 新建与整理
 */
@AnAlias("Zuls")
public class Zuls implements ZulConstants
{
    /**
     * 生成48位的文件MD5码，由fileMd5+fileLength组成
     * 
     * @param fileMd5       文件MD5码
     * @param fileLength    文件长度
     * @return
     */
    public static String buildFileMd5(String fileMd5, long fileLength)
    {
        return fileMd5.toLowerCase() + Hexs.toHexString(Bytes.BU.toBytes(fileLength), LetterCase.LOWER);
    }
    
    /**
     * 获取文件上传根目录
     * 
     * @param request       文件存储对应的请求（这个和配置大文件上传根路径有关）
     * @return              文件上传根目录
     */
    public static String getUploadRootDir(HttpRequest request)
    {
        return getUploadRootDir(request.getContext());
    }
    
    /**
     * 获取文件上传根目录
     * 
     * @param context       文件存储对应的环境（这个和配置大文件上传根路径有关）
     * @return              文件上传根目录
     */
    public static String getUploadRootDir(HttpContext context)
    {
        return context.getAttribute(ZulBootstrap.class).getUploadRootDir();
    }
    
    /********************************************************************************************/
    //通过文件编号或对象，找到文件绝对路径
    /********************************************************************************************/

    /**
     * 获取文件绝对路径
     * 
     * @param request       文件存储对应的请求（这个和配置大文件上传根路径有关）
     * @param file          文件对象
     * @return              文件绝对路径
     */
    public static String getFilePath(HttpRequest request, UpllFile file)
    {
        return getFilePath(request.getContext(), file);
    }
    
    /**
     * 获取文件绝对路径
     * 
     * @param context       文件存储对应的环境（这个和配置大文件上传根路径有关）
     * @param file          文件对象
     * @return              文件绝对路径
     */
    public static String getFilePath(HttpContext context, UpllFile file)
    {
        return context.getAttribute(ZulBootstrap.class).getFileCanonicalPath(file.getFileRelativePath());
    }
    
    /********************************************************************************************/
    //外部保存文件，指定文件内容，文件路径和文件类型
    /********************************************************************************************/

    /**
     * 提供给外部保存文件，文件大小不要太大
     * 
     * @param request           HTTP请求
     * @param data              文件数据
     * @param fileCanonicalPath 保存的文件绝对路径，要求在大文件上传配置的根目录之下
     * @param contentType       文件内容类型
     * @throws Exception        异常
     */
    public static UpllFile saveFile(HttpRequest request, byte[] data, String fileCanonicalPath, String contentType) throws Exception
    {
        return saveFile(request.getContext(), data, fileCanonicalPath, contentType);
    }
    
    /**
     * 提供给外部保存文件，文件大小不要太大
     * 
     * @param request           HTTP请求
     * @param ormId             
     * @param data              文件数据
     * @param fileCanonicalPath 保存的文件绝对路径，要求在大文件上传配置的根目录之下
     * @param contentType       文件内容类型
     * @throws Exception        异常
     */
    public static UpllFile saveFile(HttpContext context, byte[] data, String fileCanonicalPath, String contentType) throws Exception
    {
        String uploadRootDir = context.getAttribute(ZulBootstrap.class).getUploadRootDir();
        if (!Strings.startsWith(fileCanonicalPath, uploadRootDir))
        {
            throw Asserts.exception("保存的路径必须在根目录之下，否则不允许保存");
        }
        
        String fileId = Ids.uuid();
        String fileExt = Files.getFileExt(fileCanonicalPath);
        String fileName = Files.getFileName(fileCanonicalPath);
        long fileLength = data.length;
        String fileMd5 = Zuls.buildFileMd5(MD5.encode(data), fileLength);
        String fileRelativePath = Strings.removeStartsWith(fileCanonicalPath, uploadRootDir);
        
        //写入文件，写之前目录make一下
        Files.mkDirectory(Files.getFileDir(fileCanonicalPath));
        Files.write(fileCanonicalPath, data);
        
        //保存到数据库
        UpllFile file = new UpllFile();
        file.setFileId(fileId);
        file.setFileName(fileName);
        file.setFileExt(fileExt);
        file.setFileLength(fileLength);
        file.setFileMd5(fileMd5);
        file.setFileRelativePath(fileRelativePath);
        file.setFileUrl(_PATH_UPLOAD_LARGE_PREFIX_ + fileId + Files.fixFileExt(fileExt));
        file.setFileTime(Sqls.nowTimestamp());
        file.setContentType(contentType);

        ORM.table().replace(file, new HashMapSS());
        
        return file;
    }
    
    /**
     * 删除文件，并忽略异常
     * 
     * @param fileId            文件编号
     * @param fileCanonicalPath 文件绝对路径
     */
    public static void deleteFile(String fileId, String fileCanonicalPath)
    {
        try
        {
            Files.deleteFile(fileCanonicalPath);
            ORM.table().delete(UpllFile.class, fileId);
        }
        catch (Exception e)
        {
        }
    }
}
